﻿using System.Collections.Generic;
using Gameplay.PVE.Utils;
using UnityEngine;
using XLua;
using TargetCoordinateType = Yoozoo.Gameplay.RTS.Proto.TargetCoordinateType;
using ProtoTargetCoordinateType = com.youzu.warh.protocol.TargetCoordinateType;
namespace Yoozoo.Gameplay.RTS
{
    public class MarchBesiegeManager
    {
        public static bool isDebug = false;
        private static MarchBesiegeManager p_instance;
        public static MarchBesiegeManager Instance
        {
            get
            {
                if (p_instance == null)
                {
                    p_instance = new MarchBesiegeManager();
                }
                return p_instance;
            }
        }
        private class BesiegeData
        {
            public long tid;
            public int unitType;
            public float radius;
            public float angle;
            public int singleRoundMaxUnit;
            /// <summary>
            /// 当前的步长
            /// </summary>
            public float angleStep;
            public int round;
            //key角度，因为浮点数计算会有误差，因此用int * 10去表示
            public Dictionary<int, Troop> attackers;
            public Dictionary<Troop, float> attackersId;
            public HashSet<Troop> serverAttackersId;
            public bool isServerAngleReached = false;
            public Troop firstAttackerId;
            public float angleOffset;//夹击的偏移量
            public Vector3 firstAngleForward;
        }
        
        private Dictionary<Troop, BesiegeData> marches;

        private Dictionary<int, MapUnitBesiegeConfig> besiegeConfigs;

        private Dictionary<Troop, Troop> attackTargetsCache;

        private Dictionary<Troop, long> addAttackerThisFrame = new Dictionary<Troop, long>();

        private Dictionary<Troop, Dictionary<Troop,long>> attackerReachMap;
        private Dictionary<Troop, Troop> attackerAndTargetMap;

        //key表示被攻击的id，value表示哪些人正在以他为目标
        private Dictionary<Troop, HashSet<Troop>> beAttackedMap = new Dictionary<Troop, HashSet<Troop>>();
        //key表示攻击方的id，value表示被攻击放的id
        private Dictionary<Troop, Troop> attackMap = new Dictionary<Troop, Troop>();

        public void Initialize()
        {
            marches = new Dictionary<Troop, BesiegeData>();
            attackTargetsCache = new Dictionary<Troop, Troop>();
            attackerReachMap = new Dictionary<Troop, Dictionary<Troop,long>>();
            attackerAndTargetMap = new Dictionary<Troop, Troop>();
        }

        public void Dispose()
        {
            marches.Clear();
            attackTargetsCache.Clear();
            addAttackerThisFrame.Clear();
            beAttackedMap.Clear();
            attackMap.Clear();
            attackerReachMap.Clear();
            attackerAndTargetMap.Clear();
        }

        public void LoadBesiegeConfig(LuaTable table)
        {
            PveUtils.PreloadRpgConfig(table,ref besiegeConfigs,"unit_type");
        }

        public void LateUpdate()
        {
            addAttackerThisFrame.Clear();
        }
        
        private BesiegeData CreateBesiegeData(int unitType, long tid)
        {
            if (!besiegeConfigs.TryGetValue(unitType, out var config))
            {
                return null;
            }
            var attackers = new Dictionary<int, Troop>();
            var angleStep = config.angle * config.round;
            //for (float i = 0; i < 360; i += angleStep)
            //{
            //    attackers.Add(i,0);
            //}
            var data = new BesiegeData
            {
                tid = tid,
                unitType = unitType,
                radius = config.collision_radius,
                angle = config.collision_radius,
                angleStep = angleStep,
                attackers = attackers,
                attackersId = new Dictionary<Troop, float>(),
                serverAttackersId = new HashSet<Troop>(),
                singleRoundMaxUnit = (int)(360 / config.angle / config.round),
                angleOffset = 0,
                isServerAngleReached = false,
            };
            if (config.angle == 0)
            {
                Debug.LogError(unitType+"的夹击角度为0");
                data.angle = 30;
            }
            return data;
        }

        public void AddReachTime(long attackerId,TargetCoordinateType attackerType,long targetId,TargetCoordinateType type,long reachTime)
        {
            var targetTroop = new Troop(targetId, type);
            if (!attackerReachMap.TryGetValue(targetTroop, out var map))
            {
                map = new Dictionary<Troop,long>();
                attackerReachMap.Add(targetTroop,map);
            }

            if (isDebug)
            {
                Debug.LogError(attackerId +"加入到达队列");
            }
            var troop = new Troop(attackerId,attackerType);
            map[troop] = reachTime;
            attackerAndTargetMap[troop] = targetTroop;
        }

        public void RemoveReachTime(long attackerId,TargetCoordinateType attackType)
        {
            var attackTroop = new Troop(attackerId, attackType);
            if (attackerAndTargetMap.TryGetValue(attackTroop,out var targetId))
            {
                if (attackerReachMap.TryGetValue(targetId, out var map))
                {
                    if (isDebug)
                    {
                        Debug.LogError(attackerId +"移除到达队列");
                    }
                    map.Remove(attackTroop);
                }
                attackerAndTargetMap.Remove(attackTroop);
            }
        }


        private bool RemoveAttackerReachInSameTime(long attacker,TargetCoordinateType attackType, long targetId,TargetCoordinateType targetType)
        {
            var attackTroop = new Troop(attacker, attackType);
            var targetTroop = new Troop(targetId,targetType);
            bool hasSame = false;
            if (attackerReachMap.TryGetValue(targetTroop, out var map))
            {
                if (map.TryGetValue(attackTroop, out var time))
                {
                    foreach (var item in map)
                    {
                        
                        if (!item.Key.Equals(attackTroop) && Mathf.Abs(time - item.Value) <= 2000)
                        {
                            hasSame = true;
                            if (isDebug)
                            {
                                Debug.LogError(attacker+"跟"+item.Key+"有可能撞车");
                            }
                            RemoveAttacker(item.Key.Uid,item.Key.Type,true);
                        }
                    }
                }
            }
            return hasSame;
        }

        private int AngleFloatToInt(float angle)
        {
            return Mathf.RoundToInt(angle * 10);
        }
        
        private float AngleIntToFloat(int angle)
        {
            return angle / 10f;
        }
        
        
        //设置服务器的目标,如果是首次服务器夹角，则以他为入射角
        public void AddAttackTargetFromServer(int targetUnitType,long attackerId,TargetCoordinateType type, long targetId,TargetCoordinateType targetType,double besiegeAngle)
        {
            var targetTroop = new Troop(targetId,targetType);
            var attackTroop = new Troop(attackerId,type);
            if (!marches.TryGetValue(targetTroop, out var target))
            {
                target = CreateBesiegeData(targetUnitType,targetId);
                if (target == null)
                {
                    return;
                }
                marches[targetTroop] = target;
            }
            attackTargetsCache[attackTroop] = targetTroop;

            var angle = (float)besiegeAngle;
            if (!target.isServerAngleReached)
            {
                target.angleOffset = angle;
                target.attackers.Clear();
                target.attackersId.Clear();
                target.serverAttackersId.Clear();
                //Debug.LogError($"target.angleOffset {target.angleOffset}");
                for (float i = 0; i < 360; i += target.angleStep)
                {
                    var finalAngleKey = i + target.angleOffset;
                    if (finalAngleKey < 0)
                    {
                        finalAngleKey += 360;
                    }
                    else if(finalAngleKey >= 360)
                    {
                        finalAngleKey -= 360;
                    }

                    var key = AngleFloatToInt(finalAngleKey);
                    target.attackers[key] = new Troop(0, (TargetCoordinateType) 0);
                }

                var angleOffsetKey = AngleFloatToInt(target.angleOffset);
                target.attackers[angleOffsetKey] = attackTroop;
                target.attackersId[attackTroop] = target.angleOffset;
                target.firstAttackerId = attackTroop;
                target.firstAngleForward = Quaternion.AngleAxis(-target.angleOffset, Vector3.up) * Vector3.right;
                target.isServerAngleReached = true;
                if (isDebug)
                {
                    Debug.LogError(attackerId + "服务器夹击的第一个位置，入射角=" + angle);
                }
            }
            else
            {
                var angleOffsetKey = AngleFloatToInt(angle);
                //先把原来自己的id删了
                if (target.attackersId.TryGetValue(attackTroop, out var lastAngle) && AngleFloatToInt(lastAngle) != angleOffsetKey)
                {
                    target.attackers[AngleFloatToInt(lastAngle)] = new Troop(0,(TargetCoordinateType)0);
                    target.attackersId.Remove(attackTroop);
                    if (isDebug)
                    {
                        Debug.LogError(attackerId+"由于服务器夹角来了所以删除原先的夹角"+lastAngle);
                    }
                }
                target.attackers[angleOffsetKey] = attackTroop;
                target.attackersId[attackTroop] = angle;
                if (isDebug)
                {
                    Debug.LogError(attackerId+"由于服务器进入了夹击列表，角度="+angle +" 当前夹击数"+target.attackersId.Count);
                }
            }
            target.serverAttackersId.Add(attackTroop);
        }
        
        /// <summary>
        /// 客户端计算夹击位，用于提前模拟服务器走位
        /// </summary>
        /// <param name="targetUnitType"></param>
        /// <param name="targetId"></param>
        /// <param name="attackerId"></param>
        /// <param name="finalPosition"></param>
        public void AddAttacker(int targetUnitType, long targetId,TargetCoordinateType targetType,long attackerId,TargetCoordinateType attackType,Vector3 finalPosition,Vector3 centerPosition)
        {
            var targetTroop = new Troop(targetId,targetType);
            var attackTroop = new Troop(attackerId,attackType);
            if (!marches.TryGetValue(targetTroop, out var target))
            {
                target = CreateBesiegeData(targetUnitType,targetId);
                if (target == null)
                {
                    return;
                }
                marches[targetTroop] = target;
            }

            //检查这一帧有没有其他人对目标添加过夹击单位，如果有，则所有攻击这个人的夹击单位都不会寻找夹击位，防止和服务器计算顺序不一致
            //if (addAttackerThisFrame.TryGetValue(targetId, out var lastAttackerId))
            if (RemoveAttackerReachInSameTime(attackerId,attackType,targetId,targetType))
            {
                if (isDebug)
                {
                     Debug.LogError(attackerId+"因为跟别人一起夹击而开摆");
                }
                //RemoveAttacker(attackerId);
                return;
            }
            
            if (target.attackersId.Count >= target.singleRoundMaxUnit)
            {
                //超过了一圈的最大值，那就不计算了
                return;
            }

            if (attackTargetsCache.TryGetValue(attackTroop,out var originTargetId))
            {
                if (!attackTroop.Equals(originTargetId))
                {
                    RemoveAttacker(attackerId,attackType,true);
                }
                else
                {
                    return;
                }
            }

            attackTargetsCache[attackTroop] = targetTroop;
            if (target.attackersId.ContainsKey(attackTroop))
            {
                return;
            }
            
            //寻找夹击位
            var targetMarchPosition = centerPosition;
            var targetMarch = ArmyManager.Instance.MarchManager.GetMarchEntityByIdAndType(targetId,targetType);
            var attackerMarch = ArmyManager.Instance.MarchManager.GetMarchEntityByIdAndType(attackerId,attackType);
            if (attackerMarch == null)
            {
                return;
            }

            if (targetMarch != null)
            {
                targetMarchPosition = targetMarch.Position;
            }
            
            //找到离他最近的角度,往两边扩散寻找没有人的角度
            if (target.attackersId.Count == 0)
            {
                var angle = PveUtils.GetAngleServer(Vector3.right, finalPosition - targetMarchPosition);//获得第一个部队与Vector3的夹角 作为夹击角度偏移
                target.angleOffset = angle;
                //Debug.LogError($"target.angleOffset {target.angleOffset}");
                for (float i = 0; i < 360; i += target.angleStep)
                {
                    var finalAngleKey = i + target.angleOffset;
                    if (finalAngleKey < 0)
                    {
                        finalAngleKey += 360;
                    }
                    else if(finalAngleKey >= 360)
                    {
                        finalAngleKey -= 360;
                    }
                    target.attackers.Add(AngleFloatToInt(finalAngleKey), new Troop(0,(TargetCoordinateType)0));
                }

                var angleOffsetKey = AngleFloatToInt(target.angleOffset);
                target.attackers[angleOffsetKey] = attackTroop;
                target.attackersId[attackTroop] = target.angleOffset;
                target.firstAttackerId = attackTroop;
                target.angleOffset = angle;
                target.firstAngleForward = finalPosition - targetMarchPosition;
                if (isDebug)
                {
                    Debug.LogError(attackerId+"占据了夹击的第一个位置，入射角="+angle+" " + finalPosition + " " + targetMarchPosition);
                }
            }
            else
            {
                var angle = PveUtils.GetAngleServer(target.firstAngleForward, finalPosition - targetMarchPosition);
                var fixedAngle = Mathf.RoundToInt(angle / target.angleStep) * target.angleStep + target.angleOffset;
                if (fixedAngle >= 360)
                {
                    fixedAngle -= 360;
                    angle -= 360;
                }
                else if (fixedAngle < 0)
                {
                    fixedAngle += 360;
                    angle += 360;
                }
                
                for (int i = 0; i < 180 / target.angleStep; i++)
                {
                    //正
                    float angleDiff = -1;
                    var candidate = fixedAngle + i * target.angleStep;
                    if (candidate >= 360)
                    {
                        candidate -= 360;
                    }
                    else if (candidate < 0)
                    {
                        candidate += 360;
                    }
                    var candidateKey = AngleFloatToInt(candidate);
                    if (!target.attackers.ContainsKey(candidateKey))
                    {
                        Debug.LogError(candidate);
                    }
                    else if (target.attackers[candidateKey].Uid == 0)
                    {
                        angleDiff = Mathf.Abs(angle + fixedAngle- candidate);
                        if (angleDiff > 180)
                        {
                            angleDiff = 360 - angleDiff;
                        }
                    }
                    //反
                    float angleDiffR = -1;
                    var candidateR = fixedAngle - i * target.angleStep;
                    if (candidateR < 0)
                    {
                        candidateR += 360;
                    }
                    else if (candidateR >= 360)
                    {
                        candidateR -= 360;
                    }
                    var candidateKeyR = AngleFloatToInt(candidateR);
                    if (!target.attackers.ContainsKey(candidateKeyR))
                    {
                        Debug.LogError(candidateR);
                    }
                    else if (target.attackers[candidateKeyR].Uid == 0)
                    {
                        angleDiffR = Mathf.Abs(angle + fixedAngle - candidateR);
                        if (angleDiffR > 180)
                        {
                            angleDiffR = 360 - angleDiffR;
                        }
                    }

                    var realCandidate = -1f;
                    if (angleDiffR >= 0 && angleDiff >= 0)
                    {
                        realCandidate = angleDiff < angleDiffR ? candidate : candidateR;
                    }
                    else if (angleDiffR >= 0 && angleDiff < 0)
                    {
                        realCandidate = candidateR;
                    }
                    else if (angleDiffR < 0 && angleDiff >= 0)
                    {
                        realCandidate = candidate;
                    }

                    if (realCandidate >= 0)
                    {
                        var realCandidateKey = AngleFloatToInt(realCandidate);
                        target.attackers[realCandidateKey] = attackTroop;
                        target.attackersId[attackTroop] = realCandidate;
                        break;
                    }
                }

                if (isDebug)
                {
                    Debug.LogError(attackerId+"原始角度"+angle+ " 修正后角度"+fixedAngle+" 自己位置"+finalPosition+" 目标位置"+targetMarchPosition);
                }
            }

            if (isDebug)
            {
                Debug.LogError(attackerId+"加入了夹击列表, 当前的夹击数"+target.attackersId.Count + " 角度:"+target.attackersId[attackTroop]);
            }
            //if (target.attackersId.Count == 1)
            //{
            //    target.firstAttackerId = attackerId;
            //    target.angleOffset = angle;

            //    for (float i = 0; i < 360; i += target.angleStep)
            //    {
            //        target.attackers[i] +=  (long)angle;
            //    }
            //}
            
            //Debug.LogError(attackerId+"正在夹击"+targetId);
            addAttackerThisFrame[targetTroop] = attackerId;
        }

        public void RemoveAttacker(long attackerId,TargetCoordinateType attackType,bool fromInternal = false)
        {
            var troop = new Troop(attackerId,attackType);
            if (attackTargetsCache.TryGetValue(troop, out var targetId))
            {
                if (marches.TryGetValue(targetId, out var target))
                {
                    if (target.attackersId.TryGetValue(troop, out var angle))
                    {
                        if (fromInternal && target.serverAttackersId.Contains(troop))
                        {
                            //服务器的夹角，内部不删除
                            return;
                        }
                        target.attackersId.Remove(troop);
                        target.attackers[AngleFloatToInt(angle)] = new Troop(0,(TargetCoordinateType)0);
                    }
                    target.serverAttackersId.Remove(troop);
                    if (target.attackersId.Count == 0)
                    {
                        target.attackers.Clear();
                        target.isServerAngleReached = false;
                    }
                    else
                    {
                        //选择一个登基
                        if (target.firstAttackerId.Equals(troop))
                        {
                            Troop candidate = new Troop();
                            //优先从服务器给定的夹角里选
                            if (target.serverAttackersId.Count > 0)
                            {
                                foreach (var id in target.serverAttackersId)
                                {
                                    candidate = id;
                                    break;
                                }
                            }
                            else
                            {
                                foreach (var id in target.attackersId.Keys)
                                {
                                    candidate = id;
                                    break;
                                }
                            }

                            if (candidate.Uid >= 0)
                            {
                                target.firstAttackerId = candidate;
                                target.angleOffset = target.attackersId[candidate];
                                target.firstAngleForward = Quaternion.AngleAxis(-target.angleOffset, Vector3.up) * Vector3.right;
                            }
                            else
                            {
                                Debug.LogError("没有找到登基的太子");
                            }
                        }
                    }
                    if (isDebug)
                    {
                        Debug.LogError(attackerId + "移除了夹击列表," + " 当前的夹击数"+target.attackersId.Count);
                    }
                }
                attackTargetsCache.Remove(troop);
            }
        }
        

        public float GetBesiegeFinalAngle(long attackerId,TargetCoordinateType type)
        {
            var troop = new Troop(attackerId,type);
            if (attackTargetsCache.TryGetValue(troop, out var targetId))
            {
                if (marches.TryGetValue(targetId, out var target))
                {
                    if (target.firstAttackerId.Equals(troop))
                    {
                        return -1;
                    }
                    if (target.attackersId.TryGetValue(troop,out var angle))
                    {
                        //Debug.LogError(attackerId + "模拟夹击的入射角 "+ target.angleOffset);
                        return angle;
                    }
                    else
                    {
                        return -1;
                    }
                }
            }
            return -1;
        }

        public bool IsBesiegeFromClient(long attackerId,TargetCoordinateType type)
        {
            var troop = new Troop(attackerId,type);
            if (attackTargetsCache.TryGetValue(troop, out var targetId))
            {
                if (marches.TryGetValue(targetId, out var target))
                {
                    return !target.serverAttackersId.Contains(troop);
                }
            }
            return false;
        }


        /// <summary>
        /// 设置攻击目标（仅仅设置）
        /// </summary>
        public void AddAttackTarget(long attackerId,TargetCoordinateType attackType, long targetId,TargetCoordinateType targetType)
        {
            var attackerTroop = new Troop(attackerId,attackType);
            var targetTroop = new Troop(targetId,targetType);
            if (attackMap.TryGetValue(attackerTroop, out var originTarget))
            {
                if (originTarget.Equals(targetTroop))
                {
                    return;
                }
            }
            RemoveAttackTarget(attackerId,attackType);
            if (!beAttackedMap.TryGetValue(targetTroop,out var map))
            {
                map = new HashSet<Troop>();
                beAttackedMap.Add(targetTroop,map);
            }
            map.Add(attackerTroop);
            attackMap[attackerTroop] = targetTroop;
        }

        /// <summary>
        /// 移除攻击目标
        /// </summary>
        public void RemoveAttackTarget(long attackerId,TargetCoordinateType type)
        {
            var troop = new Troop(attackerId,type);
            if (attackMap.TryGetValue(troop,out var targetId))
            {
                attackMap.Remove(troop);
                if (beAttackedMap.TryGetValue(targetId, out var map))
                {
                    map.Remove(troop);
                }
            }
        }

        public HashSet<Troop> GetAttackerIdByTargetId(long targetId,TargetCoordinateType type)
        {
            if (beAttackedMap.TryGetValue(new Troop(targetId,type), out var map))
            {
                return map;
            }
            return null;
        }
        
        #if UNITY_EDITOR
        [BlackList]
        public void ShowTargetDataInspector(long id,TargetCoordinateType type)
        {
            if (marches.TryGetValue(new Troop(id,type), out var data))
            {
                UnityEditor.EditorGUILayout.BeginVertical();
                UnityEditor.EditorGUILayout.LabelField("夹击攻击者:");
                foreach (var item in data.attackersId)
                {
                    var isFirst = item.Key.Equals(data.firstAttackerId);
                    var firstStr = "";
                    if (isFirst)
                    {
                        firstStr = "(第一个夹击者）";
                    }

                    if (data.serverAttackersId.Contains(item.Key))
                    {
                        firstStr += "(真实）";
                    }
                        
                    UnityEditor.EditorGUILayout.LabelField(item.Key+" 角度:"+item.Value+firstStr);
                }
                
                UnityEditor.EditorGUILayout.LabelField("夹击角度:");
                foreach (var item in data.attackers)
                {
                    var isFirst = item.Value.Equals(data.firstAttackerId);
                    var firstStr = "";
                    if (isFirst)
                    {
                        firstStr = "(入射角）";
                    }

                    if (data.serverAttackersId.Contains(item.Value))
                    {
                        firstStr += "(真实）";
                    }
                        
                    UnityEditor.EditorGUILayout.LabelField(AngleIntToFloat(item.Key) + " :" + item.Value + firstStr);
                }
                UnityEditor.EditorGUILayout.EndVertical();
            }
            
        }
        #endif
        
    }
}
